import _ from "lodash";
import { addTicks, DialectOptions, FKRow, makeCondition } from "./dialect-options";

export const mssqlOptions: DialectOptions = {
  name: 'mssql',
  hasSchema: true,
  /**
   * Generates an SQL query that returns all foreign keys of a table.
   *
   * @param  {String} tableName  The name of the table.
   * @param  {String} schemaName The name of the schema.
   * @return {String}            The generated sql query.
   */
  getForeignKeysQuery: (tableName: string, schemaName: string) => {
    return `SELECT ccu.TABLE_NAME AS source_table,
                   ccu.CONSTRAINT_NAME AS constraint_name,
                   ccu.CONSTRAINT_SCHEMA AS source_schema,
                   ccu.COLUMN_NAME AS source_column,
                   kcu.TABLE_NAME AS target_table,
                   kcu.TABLE_SCHEMA AS target_schema,
                   kcu.COLUMN_NAME AS target_column,
                   tc.CONSTRAINT_TYPE AS constraint_type,
                   c.IS_IDENTITY AS is_identity
              FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc
             INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu
                ON ccu.CONSTRAINT_NAME = tc.CONSTRAINT_NAME
              LEFT JOIN INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
                ON ccu.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
              LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu
                ON kcu.CONSTRAINT_NAME = rc.UNIQUE_CONSTRAINT_NAME
               AND tc.CONSTRAINT_TYPE = 'FOREIGN KEY'
             INNER JOIN sys.columns c
                ON c.name = ccu.COLUMN_NAME
               AND c.object_id = OBJECT_ID(ccu.TABLE_SCHEMA + '.' + ccu.TABLE_NAME)
             WHERE tc.CONSTRAINT_TYPE != 'CHECK'
               AND ccu.TABLE_NAME = ${addTicks(tableName)}
                   ${makeCondition('ccu.TABLE_SCHEMA', schemaName)}`;
  },

  /**
   * Generates an SQL query that tells if this table has triggers or not. The
   * result set returns the total number of triggers for that table. If 0, the
   * table has no triggers.
   *
   * @param  {String} tableName  The name of the table.
   * @param  {String} schemaName The name of the schema.
   * @return {String}            The generated sql query.
   */
  countTriggerQuery: (tableName: string, schemaName: string) => {
    // NOTE: MS SQL Server does not support information_schema.triggers as of August 2019.
    // https://docs.microsoft.com/en-us/sql/relational-databases/system-information-schema-views/system-information-schema-views-transact-sql
    // When it is supported, countTriggerGeneric() could be used instead, but it is better
    // to keep backwards compatibility.
    const qname = addTicks((schemaName ? schemaName + "." : "") + tableName);
    return `SELECT COUNT(0) AS trigger_count
              FROM sys.objects tr,  sys.objects tb
             WHERE tr.type = 'TR'
               AND tr.parent_object_id = tb.object_id
               AND tb.object_id = OBJECT_ID(${qname})`;
  },
  /**
   * Determines if record entry from the getForeignKeysQuery
   * results is an actual foreign key
   *
   * @param {Object} record The row entry from getForeignKeysQuery
   * @return {Bool}
   */
  isForeignKey: (record: FKRow) => {
    return _.isObject(record) && _.has(record, 'constraint_type') && record.constraint_type === 'FOREIGN KEY';
  },

  /**
   * Determines if record entry from the getForeignKeysQuery
   * results is a unique key
   *
   * @param {Object} record The row entry from getForeignKeysQuery
   * @return {Bool}
   */
  isUnique: (record: FKRow, records: FKRow[]) => {
    return _.isObject(record) && _.has(record, 'constraint_type') && record.constraint_type === 'UNIQUE';
  },

  /**
   * Determines if record entry from the getForeignKeysQuery
   * results is an actual primary key
   *
   * @param {Object} record The row entry from getForeignKeysQuery
   * @return {Bool}
   */
  isPrimaryKey: (record: FKRow) => {
    return _.isObject(record) && _.has(record, 'constraint_type') && record.constraint_type === 'PRIMARY KEY';
  },

  /**
   * Determines if record entry from the getForeignKeysQuery
   * results is an actual serial/auto increment key
   *
   * @param {Object} record The row entry from getForeignKeysQuery
   * @return {Bool}
   */
  isSerialKey: (record: FKRow) => {
    return (
      _.isObject(record) && mssqlOptions.isPrimaryKey(record) && (_.has(record, 'is_identity') && record.is_identity)
    );
  },
  /**
   * Override Sequelize's method for showing all tables to allow schema support.
   * See sequelize/lib/dialects/mssql/query-generator.js:showTablesQuery()
   * @param {String} schemaName Optional. The schema from which to list tables.
   * @return {String}
   */
  showTablesQuery: (schemaName?: string) => {
    return `SELECT TABLE_NAME AS table_name, TABLE_SCHEMA AS table_schema
              FROM INFORMATION_SCHEMA.TABLES
             WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME != 'sysdiagrams'
                   ${makeCondition("TABLE_SCHEMA", schemaName)}`;
  },

  showViewsQuery: (schemaName?: string) => {
    return `SELECT TABLE_NAME AS table_name, TABLE_SCHEMA AS table_schema
              FROM INFORMATION_SCHEMA.TABLES
             WHERE TABLE_TYPE = 'VIEW'
                  ${makeCondition("TABLE_SCHEMA", schemaName)}`;
  },

  /** Sequelize "describeTable" doesn't include precision and scale in mssql */
  showPrecisionQuery: (tableName: string, schemaName?: string) => {
    return `SELECT COLUMN_NAME AS column_name, NUMERIC_PRECISION AS numeric_precision, NUMERIC_SCALE AS numeric_scale
    FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${tableName}'` + (!schemaName ? '' : ` AND TABLE_SCHEMA = '${schemaName}'`);
  },


};
